/***********************************************************************************
Copy right:	    hqyj Tech.
Author:         jiaoyue
Date:           2023.07.01
Description:    http请求处理
***********************************************************************************/
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "custom_handle.h"
#include <unistd.h>
#include <stdio.h>
#include <modbus.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "cJSON.h"
#include "cJjSON.h"

#define KB 1024
#define HTML_SIZE (64 * KB)

// 普通的文本回复需要增加html头部
#define HTML_HEAD "Content-Type: text/html\r\n" \
                  "Connection: close\r\n"
union val_t
{ 
    int b_val;  //bool类型存储空间
    int i_val;   //整形值存储空间
    float f_val;  //浮点值存储空间
};

struct control
{
    long Equ;      // 消息类型
    int key;       // 设备ID
    char name[32]; //设备名称
    union val_t val;  //数据设置
    int addr;      // 线圈寄存器
    int type;      // 数据点值的类型，1：bool类型 2：int型  3：float型
};
struct control ctr;
struct data32
{
    int key;
    char name[32];
    union val_t val;  //实时数据
    int type;        //数据类型
};

struct datam
{
    int key;
    char name[32];
    union val_t val;  //实时数据
    int addr;         //modbus地址
    int type;        //数据类型
};

typedef struct data
{
    struct data32 d32[10];
    struct datam  dmod[10];
    int arrsize;
}shmdata;
extern struct shm_param para; // 共享内存的首地址
extern int *total;            // 指向共享内存中数据节点总个数  6  4
extern struct data *shmData;      // 指向共享内存中节点缓存数组头
extern void *node_p;

/**
 * 处理post的请求数据中的登录请求
 * 参数:accepted 套接字  post的请求数据
 * 返回值: int类型的数值
 */
static int handle_login(int sock, const char *input)
{
    char reply_buf[HTML_SIZE] = {0};
    //  字符数组 的 大小 64 kb
    char *uname = strstr(input, "username=");
    /**
     * char *strstr(const char *haystack, const char *needle);
     * 函数确实返回指向 haystack 中第一次出现的 needle 字符串的指针，如果没有找到，则返回 NULL。
     */
    uname += strlen("username=");
    // 移动指针位置到 实际用户名数据的首地址
    char *p = strstr(input, "password");
    // 将第一次出现password字符串位置的前一个字节设置成 \0  （也就是将 & 符 变成 \0）
    // 到这个分割符时%s结束
    // username=JohnDoe&password=password123

    *(p - 1) = '\0';
    printf("username = %s\n", uname); // 打印输入的用户名

    // 移动指针位置到 实际密码数据的首地址
    char *passwd = p + strlen("password=");

    printf("passwd = %s\n", passwd); // 打印输入的密码

    // 对账号和密码进行比对：
    if (strcmp(uname, "qs") == 0 && strcmp(passwd, "123456") == 0)
    {
        sprintf(reply_buf, "<script>localStorage.setItem('usr_user_name', '%s');</script>", uname);
        strcat(reply_buf, "<script>window.location.href = '/index.html';</script>");
        send(sock, reply_buf, strlen(reply_buf), 0);
        // 如果用户名和密码都匹配，则构造一个HTML脚本字符串，
        // 使用localStorage.setItem将用户名存储在浏览器的本地存储中，并通过JavaScript重定向到/index.html页面。
    }
    else
    {
        printf("web login failed\n");
        //"用户名或密码错误"提示，chrome浏览器直接输送utf-8字符流乱码，没有找到太好解决方案，先过渡
        char out[128] = {0xd3, 0xc3, 0xbb, 0xa7, 0xc3, 0xfb, 0xbb, 0xf2, 0xc3,
                         0xdc, 0xc2, 0xeb, 0xb4, 0xed, 0xce, 0xf3};
        sprintf(reply_buf, "<script charset='gb2312'>alert('%s');</script>", out);
        strcat(reply_buf, "<script>window.location.href = '/login.html';</script>");
        send(sock, reply_buf, strlen(reply_buf), 0);
        // 如果用户名或密码不匹配，则打印一条失败消息到服务器端的控制台，
        // 并尝试向客户端发送一个包含错误提示的HTML脚本字符串。
    }

    return 0;
}

/**
 * 功能:执行post请求数据对应的求和函数
 * 参数:accepted 套接字  post的请求数据
 * 返回值: int类型
 */
static int handle_add(int sock, const char *input)
{
    int number1, number2;
    // input必须是"data1=1data2=6"类似的格式，注意前端过来的字符串会有双引号

    // 拆找出来的原始请求数据  req_buf = "data1=1data2=2"
    sscanf(input, "\"data1=%ddata2=%d\"", &number1, &number2);
    printf("num1 = %d\n", number1); // 1
    printf("num2 = %d\n", number2); // 2
    // 使用 sscanf 函数尝试从 input 字符串中按照 "data1=%ddata2=%d" 的格式
    // 解析出两个整数 number1 和 number2
    char reply_buf[HTML_SIZE] = {0};
    printf("num = %d\n", number1 + number2);     // int类型的 3
    sprintf(reply_buf, "%d", number1 + number2); // 3
    printf("resp = %s\n", reply_buf);            // 字符串类型的"3"
    send(sock, reply_buf, strlen(reply_buf), 0);

    return 0;
}

/**
 * 功能用于将int类型的数据挂树
 * 无返回值
 */
void aitooi(cJSON *item, cJSON *root, const char *key, int val)
{
    item = cJSON_CreateNumber(val);
    cJSON_AddItemToObject(root, key, item);
}

/**
 * 功能用于将float类型的数据挂树
 * 无返回值
 */
void aitoof(cJSON *item, cJSON *root, const char *key, float val)
{
    item = cJSON_CreateNumber(val);
    cJSON_AddItemToObject(root, key, item);
}

/**
 * 功能用于读取共享内存中的数据,转换成json格式上传给网页客户端口
 * 参数:accepted 套接字
 */
int mymodbusget(int sock)
{
    printf("开始读取共享内存\n");
    printf("----------------%d", __LINE__);
    struct shm_param para;
    shm_init(&para, "shm", sizeof(shmdata));
        struct data *shmData = (struct data *)shm_getaddr(&para);
           printf("共享内存STM32原始数据: %f %f %f %d %d %d\n",shmData->d32[1].val.f_val,shmData->d32[2].val.f_val,
           shmData->d32[3].val.f_val, shmData->d32[5].val.b_val, shmData->d32[6].val.b_val,
           shmData->d32[7].val.b_val);
           printf("共享内存Modbus原始数据: %f %f %f %d %d %d %d\n",shmData->dmod[0].val.f_val,
           shmData->dmod[1].val.f_val, shmData->dmod[3].val.f_val, shmData->dmod[4].val.b_val,
           shmData->dmod[6].val.b_val, shmData->dmod[2].val.b_val, shmData->dmod[5].val.b_val);
 cJSON *root = cJSON_CreateObject();
    cJSON *item = NULL;
    cJSON_AddItemToObject(root,"type",cJSON_CreateNumber(1));
    cJSON *data=cJSON_CreateArray();
cJSON *rooot = cJSON_CreateObject();
cJSON_AddItemToObject(rooot,"key",cJSON_CreateNumber(101));
cJSON_AddItemToObject(rooot,"val",cJSON_CreateNumber(shmData->dmod[0].val.f_val));
cJSON_AddItemToArray(data,rooot);
cJSON *roiot = cJSON_CreateObject();
cJSON_AddItemToObject(roiot,"key",cJSON_CreateNumber(102));
cJSON_AddItemToObject(roiot,"val",cJSON_CreateNumber(shmData->dmod[1].val.f_val));
cJSON_AddItemToArray(data,roiot);
cJSON *rouot = cJSON_CreateObject();
cJSON_AddItemToObject(rouot,"key",cJSON_CreateNumber(103));
cJSON_AddItemToObject(rouot,"val",cJSON_CreateNumber(shmData->dmod[2].val.b_val));
cJSON_AddItemToArray(data,rouot);
cJSON *royot = cJSON_CreateObject();
cJSON_AddItemToObject(royot,"key",cJSON_CreateNumber(104));
cJSON_AddItemToObject(royot,"val",cJSON_CreateNumber(shmData->dmod[3].val.f_val));
cJSON_AddItemToArray(data,royot);
cJSON *rotot = cJSON_CreateObject();
cJSON_AddItemToObject(rotot,"key",cJSON_CreateNumber(105));
cJSON_AddItemToObject(rotot,"val",cJSON_CreateNumber(shmData->dmod[4].val.b_val));
cJSON_AddItemToArray(data,rotot);
cJSON *rorot = cJSON_CreateObject();
cJSON_AddItemToObject(rorot,"key",cJSON_CreateNumber(106));
cJSON_AddItemToObject(rorot,"val",cJSON_CreateNumber(shmData->dmod[5].val.b_val));
cJSON_AddItemToArray(data,rorot);
cJSON *roeot = cJSON_CreateObject();
cJSON_AddItemToObject(roeot,"key",cJSON_CreateNumber(107));
cJSON_AddItemToObject(roeot,"val",cJSON_CreateNumber(shmData->dmod[6].val.b_val));
cJSON_AddItemToArray(data,roeot);


//32
cJSON *roaot = cJSON_CreateObject();
cJSON_AddItemToObject(roaot,"key",cJSON_CreateNumber(302));
cJSON_AddItemToObject(roaot,"val",cJSON_CreateNumber(shmData->d32[1].val.f_val));
cJSON_AddItemToArray(data,roaot);
cJSON *rosot = cJSON_CreateObject();
cJSON_AddItemToObject(rosot,"key",cJSON_CreateNumber(303));
cJSON_AddItemToObject(rosot,"val",cJSON_CreateNumber(shmData->d32[2].val.f_val));
cJSON_AddItemToArray(data,rosot);
cJSON *rodot = cJSON_CreateObject();
cJSON_AddItemToObject(rodot,"key",cJSON_CreateNumber(304));
cJSON_AddItemToObject(rodot,"val",cJSON_CreateNumber(shmData->d32[3].val.f_val));
cJSON_AddItemToArray(data,rodot);
cJSON *rofot = cJSON_CreateObject();
cJSON_AddItemToObject(rofot,"key",cJSON_CreateNumber(306));
cJSON_AddItemToObject(rofot,"val",cJSON_CreateNumber(shmData->d32[5].val.b_val));
cJSON_AddItemToArray(data,rofot);
cJSON *rogot = cJSON_CreateObject();
cJSON_AddItemToObject(rogot,"key",cJSON_CreateNumber(307));
cJSON_AddItemToObject(rogot,"val",cJSON_CreateNumber(shmData->d32[6].val.b_val));
cJSON_AddItemToArray(data,rogot);
cJSON *rohot = cJSON_CreateObject();
cJSON_AddItemToObject(rohot,"key",cJSON_CreateNumber(308));
cJSON_AddItemToObject(rohot,"val",cJSON_CreateNumber(shmData->d32[7].val.b_val));
cJSON_AddItemToArray(data,rohot);

cJSON_AddItemToObject(root,"data",data);
    char *p = cJSON_Print(root);
    printf("等待上传的JSON格式的字符串: %s\n", p);
    send(sock, p, strlen(p), 0);
    free(p);
    p = NULL;
    // 内存清理
    cJSON_Delete(root);
    return 1;
}

/**
 * 解析传来的json格式的post请求数据,并将其转换成消息队列,实现对设备层的控制
 * 参数:accepted 套接字  , cjson 的根节点
 */
int mymodbusset(int sock, cJSON *root)
{
    printf("解析网页端传过来的类型为2的JSON 命令\n");
    // 从root节点获取login节点
    cJSON *data = cJSON_GetObjectItem(root, "data");
    cJSON *item = NULL;
    
    // 从login节点获取pwd节点
    item = cJSON_GetObjectItem(data, "key");
    struct control send_buf;
    if(item->valueint>=101 && item->valueint <=107)
    {
        send_buf.Equ = 1;
    }
    else if(item->valueint>=301 && item->valueint <=309)
    {
        send_buf.Equ = 32;
    }
    printf("%s = %d\n", item->string, item->valueint);
    send_buf.key = item->valueint;
    item = cJSON_GetObjectItem(data, "val");
    printf("%s = %d\n", item->string, item->valueint);
    send_buf.val.b_val = item->valueint;
    if (msg_queue_send("msg", &send_buf, sizeof(send_buf), 0) < 0)
    {
        printf("msg_queue_send error\n");
        return -1;
    }
    else
        printf("发送消息队列成功！！！\n");
        printf("%ld %d\n",send_buf.Equ,send_buf.key);
    char str[] = "send ok!!!\n";
    send(sock, str, strlen(str), 0);
    cJSON_Delete(root); // 删除树
    return 1;
}

/**
 * @brief 处理get和post带参数的请求
 * @param sock：accepted 套接字  query_string：GET请求的参数 ，  input：Post请求的参数
 * @return int 类型的返回值
 */
int parse_and_process(int sock, const char *query_string, const char *input)
{

    // query_string不一定能用的到,也就是对带参的get请求未进行自定义的处理

    // 先处理登录操作
    if (strstr(input, "username=") && strstr(input, "password="))
    // 判断post的请求数据里是否既包含 账号 也包含 密码
    {
        // 执行登录函数；
        return handle_login(sock, input);
    }

    // 处理求和请求
    else if (strstr(input, "data1=") && strstr(input, "data2="))
    {
        // 执行求和函数
        return handle_add(sock, input);
    }

    else // 剩下的都是json请求，这个和协议有关了
    {

        const char *json_response = "{\"message\": \"I don't now!\"}";
        // 这里的input是json格式的请求
        printf("input = %s\n", input);
        // 将网页传来的Json反序列化
        cJSON *root = cJSON_Parse(input);
        if (NULL == root)
        {
            printf("parse err\n");
            return -1;
        }
        cJSON *item = cJSON_GetObjectItem(root, "type");
        // 创建swich case 判断 到底下发的是哪种类型的命令：
        printf("type = %d\n", item->valueint);
        switch (item->valueint)
        {
        case 1:                 // 采集模式
            mymodbusget(sock);  // 默认全部采集并且是刷新上报模式
            cJSON_Delete(root); // 删除树
            break;
        case 2: // 下发指令
            return mymodbusset(sock, root);
            // 在函数里已经完成了删除树的操作
            break;
        case 3: // 修改上报模式   // 上报模式分为三种 刷新上报 变化上报 周期上报

            break;
        case 4: // 历史记录
            break;
        default:

            send(sock, json_response, strlen(json_response), 0);
            break;
        }
        // 构建要回复的JSON数据
        // 发送HTTP响应给客户端
    }

    return 0;
}
// int parse_and_process(int sock, const char *query_string, const char *input)
// {
//     //query_string不一定能用的到

//     //先处理登录操作
//     if(strstr(input, "username=") && strstr(input, "password="))
//     {
//         return handle_login(sock, input);
//     }
//     //处理求和请求
//     else if(strstr(input, "data1=") && strstr(input, "data2="))
//     {
//         return handle_add(sock, input);
//     }
//     else  //剩下的都是json请求，这个和协议有关了
//     {
//         //这里的input是json格式的请求
//         printf("input = %s\n", input);

//         //根据协议来判断是什么请求，然后做不同的回复处理

//         // 构建要回复的JSON数据，这里模拟下
//         const char* json_response = "{\"type\": 1,\"data\":[{\"key\":303,\"val\":\"15.5\"},{\"key\":304,\"val\":\"11.8\"}, {\"key\":101,\"val\":\"45\"}, {\"key\":102,\"val\":\"77\"}, {\"key\":104,\"val\":\"99\"}]}";

//         // 发送HTTP响应给客户端
//         send(sock, json_response, strlen(json_response), 0);
//     }

//     return 0;
// }